package com.easy.core.tools;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>Description:类型转换 </p>
 * <p>CreateDate:@2013-8-21</p>
 * @author zhangj
 */
public class TypeConvert {

	public static Object convertValue(Object value, Class<?> toType) {
		return convertValue(value, toType, false);
	}
	
	/**
	 * 描述：类型转换
	 * @method convertValue
	 * @param value		原值
	 * @param toType	目标类型
	 * @param preventNulls	是否阻止null
	 * @return Object
	 */
	public static Object convertValue(Object value, Class<?> toType, boolean preventNulls) {
		Object result = null;
		
		if(null != value && toType.isAssignableFrom(value.getClass())) return value;
		
		if(null != value){
			if(value.getClass().isArray() && toType.isArray()){
				Class<?> componentType = toType.getComponentType();
				result = Array.newInstance(componentType, Array.getLength(value));
				for(int i = 0, icount = Array.getLength(value); i < icount; i++) {
					Array.set(result, i, convertValue(Array.get(value, i), componentType));
				}
			}else if(value.getClass().isArray() && !toType.isArray()){
				return convertValue(Array.get(value, 0), toType);
			}else if(!value.getClass().isArray() && toType.isArray()){
				if(toType.getComponentType() == Character.TYPE)
					result = stringValue(value).toCharArray();
				else if(toType.getComponentType() == Object.class)
					return new Object[]{value};
			}else{
				if(toType == Integer.class || toType == Integer.TYPE)
					result = new Integer((int) longValue(value));
				if(toType == Double.class || toType == Double.TYPE)
					result = new Double(doubleValue(value));
				if((toType == Boolean.class) || (toType == Boolean.TYPE))
					result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
				if((toType == Byte.class) || (toType == Byte.TYPE))
					result = new Byte((byte) longValue(value));
				if((toType == Character.class) || (toType == Character.TYPE))
					result = new Character((char) longValue(value));
				if((toType == Short.class) || (toType == Short.TYPE))
					result = new Short((short) longValue(value));
				if((toType == Long.class) || (toType == Long.TYPE))
					result = new Long(longValue(value));
				if((toType == Float.class) || (toType == Float.TYPE))
					result = new Float(doubleValue(value));
				if(toType == BigInteger.class)
					result = bigIntValue(value);
				if(toType == BigDecimal.class)
					result = bigDecValue(value);
				if(toType == String.class)
					result = stringValue(value);
			}
		}else{
			if(preventNulls && toType == Boolean.class)
				result = Boolean.FALSE;
			else if(preventNulls && Number.class.isAssignableFrom(toType))
				result = NUMERIC_DEFAULTS.get(toType);
		}
		
		if(result == null && preventNulls)
			return value;
		
		if(value != null && result == null){
			throw new IllegalArgumentException("Unable to convert type "
					+ value.getClass().getName() + " of " + value
					+ " to type of " + toType.getName());
		}
		return result;
	}
	
	public static long longValue(Object value) throws NumberFormatException{
		if (value == null) return 0L;
		Class<?> c = value.getClass();
		if(c.getSuperclass() == Number.class) return ((Number) value).longValue();
		if(c == Boolean.class) return ((Boolean) value).booleanValue() ? 1 : 0;
		if(c == Character.class) return ((Character) value).charValue();
		return Long.parseLong(stringValue(value, true));
	}
	
	public static double doubleValue(Object value) throws NumberFormatException {
		if (value == null) return 0.0;
		Class<?> c = value.getClass();
		if(c.getSuperclass() == Number.class)
			return ((Number) value).doubleValue();
		if(c == Boolean.class)
			return ((Boolean) value).booleanValue() ? 1 : 0;
		if(c == Character.class)
			return ((Character) value).charValue();
		String s = stringValue(value, true);
		return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
	}
	
	public static boolean booleanValue(Object value){
		if(value == null) return false;
		Class<?> c = value.getClass();
		if(c == Boolean.class)
			return ((Boolean) value).booleanValue();
		if(c == Character.class)
			return ((Character) value).charValue() != 0;
		if(value instanceof Number)
			return ((Number) value).doubleValue() != 0;
		if(c == String.class){
			String val = (String) value;
			if(val.length()>0){
				return "true".equalsIgnoreCase(val);
			}else return false;
		}
		return true;
	}
	
	public static BigInteger bigIntValue(Object value) throws NumberFormatException {
		if(value == null) return BigInteger.valueOf(0L);
		Class<?> c = value.getClass();
		if(c == BigInteger.class)
			return (BigInteger) value;
		if(c == BigDecimal.class)
			return ((BigDecimal) value).toBigInteger();
		if(c.getSuperclass() == Number.class)
			return BigInteger.valueOf(((Number) value).longValue());
		if(c == Boolean.class)
			return BigInteger.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
		if(c == Character.class)
			return BigInteger.valueOf(((Character) value).charValue());
		return new BigInteger(stringValue(value, true));
	}
	
	public static BigDecimal bigDecValue(Object value) throws NumberFormatException {
		if(value == null) return BigDecimal.valueOf(0L);
		Class<?> c = value.getClass();
		if(c == BigDecimal.class)
			return (BigDecimal) value;
		if(c == BigInteger.class)
			return new BigDecimal((BigInteger) value);
		if(c == Boolean.class)
			return BigDecimal.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
		if(c == Character.class)
			return BigDecimal.valueOf(((Character) value).charValue());
		return new BigDecimal(stringValue(value, true));
	}
	
	public static String stringValue(Object value) {
		return stringValue(value, false);
	}
	
	public static String stringValue(Object value, boolean trim) {
		String result;
		if(value == null){
			result = "" + null;
		}else{
			result = value.toString();
			if(trim) result = result.trim();
		}
		return result;
	}
	
	public static String stringValue(Object value, String defValue) {
		if(null == value) return defValue;
		return value.toString();
	}
	
	private static final Map<Class<?>, Object> NUMERIC_DEFAULTS = new HashMap<Class<?>, Object>();
	static {
		NUMERIC_DEFAULTS.put(Boolean.class, Boolean.FALSE);
        NUMERIC_DEFAULTS.put(Byte.class, new Byte((byte) 0));
        NUMERIC_DEFAULTS.put(Short.class, new Short((short) 0));
        NUMERIC_DEFAULTS.put(Character.class, new Character((char) 0));
        NUMERIC_DEFAULTS.put(Integer.class, new Integer(0));
        NUMERIC_DEFAULTS.put(Long.class, new Long(0L));
        NUMERIC_DEFAULTS.put(Float.class, new Float(0.0f));
        NUMERIC_DEFAULTS.put(Double.class, new Double(0.0));
        NUMERIC_DEFAULTS.put(BigInteger.class, new BigInteger("0"));
        NUMERIC_DEFAULTS.put(BigDecimal.class, new BigDecimal(0.0));
	}
	
}
